package juc;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.stream.IntStream;

/**
 * StampedLock类（Java 8新增）测试
 * 特性：
 * long型时间戳、3种模型、乐观读；
 * 不可重入；
 * 在不少情况下，可用来替代ReentrantLock、ReentrantReadWriteLock
 * @author ben
 * @date 2021-09-09 23:19:00 CST
 */
public class TestStampedLock {

	public static Consumer<Object> cs = System.out::println;
	
	public static void main(String[] args) {
//		test1();
		
//		test2();
		
		test3();
	}

	/**
	 * 乐观读（tryOptimisticRead()）
	 * 调整线程数量进行测试，Test3类中调整休眠失眠进行测试
	 * @author ben
	 * @date 2021-09-10 05:23:13 CST
	 */
	public static void test3() {
		cs.accept("test3() start");
		
		Test3 t3 = new Test3();
		
		ExecutorService es = Executors.newFixedThreadPool(10);
		
		// 写线程
		IntStream.range(0, 1).forEach(item->{
			es.execute(() -> {
				while (true) {
					cs.accept("t3 write start, now=" + System.currentTimeMillis());
					t3.write();
				}
			});
		});

		// 读线程
		IntStream.range(0, 1).forEach(item->{
			es.execute(() -> {
				while (true) {
					cs.accept("t3 read=" + t3.read());
				}
			});
		});

		cs.accept("test3() end");
	}
	
	/**
	 * 试验2：非常规使用
	 * 一些可能导致死锁的情况
	 * @author ben
	 * @date 2021-09-10 05:03:44 CST
	 */
	public static void test2() {
		cs.accept("test2() start");
		
		StampedLock sl = new StampedLock();
		
		cs.accept("sl.readLock()");
		long stamp = sl.readLock();
		// =1
		cs.accept("stamp=" + stamp + ", readLockCount=" + sl.getReadLockCount());
		stamp = sl.readLock();
		cs.accept("stamp=" + stamp);
		stamp = sl.readLock();
		cs.accept("stamp=" + stamp);
		stamp = sl.readLock();
		cs.accept("stamp=" + stamp);
		stamp = sl.readLock();
		cs.accept("stamp=" + stamp);
		stamp = sl.readLock();
		// =6
		cs.accept("stamp=" + stamp + ", readLockCount=" + sl.getReadLockCount());
		
		sl.unlockRead(stamp);
		// 减1，=5
		cs.accept("unlock...stamp=" + stamp + ", readLockCount=" + sl.getReadLockCount());
		// =true, false
		cs.accept("unlock...stamp=" + sl.isReadLocked() + ", " + sl.isWriteLocked());
		
		// 解决下面 程序卡住问题
		cs.accept("\nsl.tryUnlockRead()---循环释放readLock：");
		IntStream.range(0, sl.getReadLockCount()).forEach(item->{
			boolean tryRet = sl.tryUnlockRead();
			// =true =4、3、2、1、0
			cs.accept("tryRet=" + tryRet + ", readLockCount=" + sl.getReadLockCount());
		});
		
		// 已释放所有读锁，再次unlock——下面3中都抛出异常，程序终止
		// 异常：java.lang.IllegalMonitorStateException
//		sl.unlock(stamp);
//		sl.unlockRead(stamp);
//		sl.unlockWrite(stamp);
		
		cs.accept("\nsl.writeLock()");
		// 注意，程序卡住...
		stamp = sl.writeLock();
		cs.accept("write stamp=" + stamp + ", readLockCount=" + sl.getReadLockCount());
		
		// try1: 再次锁住writeLock：卡住...
//		stamp = sl.writeLock();
//		cs.accept("write stamp=" + stamp + ", readLockCount=" + sl.getReadLockCount());
		
		// try2: 返回0：失败
		// 但不阻塞
//		cs.accept("tryReadLock=" + sl.tryReadLock());
//		cs.accept("tryWriteLock=" + sl.tryWriteLock());
		
		// try3: tryUnlock...
		// false
//		cs.accept("tryUnlockRead=" + sl.tryUnlockRead());
//		// true
//		cs.accept("tryUnlockWrite=" + sl.tryUnlockWrite());
		
		cs.accept("test2() end");
	}
	
	/**
	 * test1()的共享数据
	 */
	private static int shareCnt = 0; 
	
	/**
	 * 试验1：基本特性
	 * 调整休眠时间可以得到不同结果
	 * @author ben
	 * @date 2021-09-09 23:23:00 CST
	 */
	public static void test1() {
		cs.accept("test1() START");
		StampedLock sl = new StampedLock();
		
		ExecutorService es = Executors.newFixedThreadPool(2);
		// 写线程
		es.execute(()->{
			while (true) {
				long stamp = sl.writeLock();
				cs.accept("sl.writeLock 1");
				try {
					shareCnt++;
					cs.accept("sl.writeLock after add, shareCnt=" + shareCnt);
					try {
						// 休眠中，读线程无法执行
						// 锁住期间，无法读写数据
						cs.accept("sl.writeLock sleep 1, ms=" + System.currentTimeMillis());
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} finally {
					sl.unlockWrite(stamp);
					cs.accept("sl.writeLock 2, ms=" + System.currentTimeMillis());
					try {
						// 休眠中，读线程可以
						TimeUnit.SECONDS.sleep(1);
						cs.accept("sl.writeLock sleep 2");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		es.execute(()->{
			while (true) {
				long stamp = sl.readLock();
				cs.accept("sl.readLock 1");
				try {
					cs.accept("sl.readLock shareCnt=" + shareCnt + ", ms=" + System.currentTimeMillis());
					try {
						// 锁住期间，无法修改数据！
						TimeUnit.SECONDS.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} finally {
					sl.unlockRead(stamp);
					cs.accept("sl.readLock 2, ms=" + System.currentTimeMillis());
				}
			}
		});
		
		cs.accept("test1() END");
	}
	
}

/**
 * 乐观读
 * @author ben
 * @date 2021-09-10 05:27:43 CST
 */
class Test3 {

	public static Consumer<Object> cs = System.out::println;

	private final StampedLock sl = new StampedLock();
	
	// 共享数据
	private int shareCnt = 0;
	
	// 乐观读、非乐观读数量
	long optimisticReadCnt = 0;
	long noOptimisticReadCnt = 0;
	
	// 试验乐观读方式 读数据
	public int read() {
		long stamp = sl.tryOptimisticRead();
		cs.accept("read: 1 stamp=" + stamp + ", now=" + System.currentTimeMillis());
		// 注意前面的 英文感叹号！
		if (!sl.validate(stamp)) {
			// 不可以乐观读，去锁住之后再读
			stamp = sl.readLock();
			cs.accept("read: 2 stamp=" + stamp + ", now=" + System.currentTimeMillis());
			try {
				noOptimisticReadCnt++;
				cs.accept("read: 3 noOptimisticReadCnt=" + noOptimisticReadCnt + ", optimisticReadCnt=" + optimisticReadCnt);
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return shareCnt;
			} finally {
				cs.accept("read: 4 stamp=" + stamp);
				sl.unlock(stamp);
			}
		}
		cs.accept("read: 5 Oh, OptimisticRead!");
		optimisticReadCnt++;
		cs.accept("read: 6 noOptimisticReadCnt=" + noOptimisticReadCnt + ", optimisticReadCnt=" + optimisticReadCnt);
		return shareCnt;
	}
	
	// 写数据
	public void write() {
		try {
			// 尝试获取1秒钟
			int readLockCount = sl.getReadLockCount();
			if (readLockCount > 0) {
				cs.accept("write: start ReadLockCount=" + sl.getReadLockCount());
			}
			// 可能返回0，此时，unlockWrite出现异常！
			// 未检查、未捕获，导致写线程挂掉！
			long stamp = sl.tryWriteLock(1, TimeUnit.SECONDS);
			cs.accept("write: 1 stamp=" + stamp);
			if (sl.validate(stamp)) {
				try {
					// 修改数据
					shareCnt++;
					cs.accept("write: 2 改");
					
//					TimeUnit.SECONDS.sleep(1);
				} finally {
					cs.accept("write: 3 shareCnt=" + shareCnt);
					sl.unlockWrite(stamp);
				}
			} else {
				cs.accept("write: 4 获取锁失败！未更新");
			}
		} catch (InterruptedException e) {
			cs.accept("write: 5 Exception: e=" + e.getMessage());
		}
	}
	
}